package poki.model;

@SuppressWarnings("javadoc")
public class HandRanking {
	
	//TODO: Es überschneiden sich vermutlich einige Ratingzonen
	//Bewertet ein Kartenset nach den 5 besten Karten (normalerweise sollten 7 Karten im übergebenen cardSet enthalten sein)
	public double evaluate(CardSet cardSet) {
		
		double score = 0;
		CardSet evaluationSet = cardSet;
		Card dummyCard;
		CardSet dummySet;
		CardSet[] dummyField;


		for(int bestCards = 0; bestCards < 5;) {
			if (bestCards < 1 && (dummySet = this.checkStraightFlush(evaluationSet)) != null) {
				score = score + dummySet.getCardList().get(0).getNumber() + 3379;		//3384 bis 3393
				bestCards = 5;

			} else if (bestCards < 2 && (dummySet = this.checkVierling(evaluationSet)) != null) {
				score = score + dummySet.getCardList().get(0).getNumber() + 3363;		//3365 bis 3378
				evaluationSet.disMerge(dummySet);
				bestCards = bestCards + 4;

			} else if (bestCards < 1 && (dummyField = this.checkFullHouse(evaluationSet)) != null) {
				score = score + (dummyField[0].getCardList().get(0).getNumber() - 1)*14;		//14 bis 182
				
				score = score + dummyField[1].getCardList().get(0).getNumber() - 1;		//1 bis 13
				
				score = score + 3167;		//3183 bis 3362
				
				bestCards = 5;
				
			} else if (bestCards < 1 && (dummySet = this.checkFlush(evaluationSet)) != null) {
				
				Card highCard = this.checkHighCard(dummySet);
				dummySet.removeCard(highCard);
				score = score + highCard.getNumber()*41371;		//41371 bis 579194
				
				highCard = this.checkHighCard(dummySet);
				dummySet.removeCard(highCard);
				score = score + highCard.getNumber()*2955;		//2955 bis 41370
				
				highCard = this.checkHighCard(dummySet);
				dummySet.removeCard(highCard);
				score = score + highCard.getNumber()*211;		//211 bis 2954
				
				highCard = this.checkHighCard(dummySet);
				dummySet.removeCard(highCard);
				score = score + highCard.getNumber()*15;	//15 bis 210
				
				highCard = this.checkHighCard(dummySet);
				dummySet.removeCard(highCard);
				score = score + highCard.getNumber();		//2 bis 14
				
				score = score/197;		//44554 bis 623742 => 226 bis 3167
				
				bestCards = 5;
				
			} else if (bestCards < 1 && (dummySet = this.checkStraight(evaluationSet)) != null) {
				score = score + dummySet.getCardList().get(0).getNumber() + 210;		//215 bis 224
				
				bestCards = 5;
				
			} else if (bestCards < 3 && (dummySet = this.checkDrilling(evaluationSet)) != null) {
				score = score + dummySet.getCardList().get(0).getNumber() + 196;		//198 bis 210
				evaluationSet = evaluationSet.disMerge(dummySet);
				
				bestCards = bestCards + 3;
				
			} else if (bestCards < 2 && (dummyField = this.checkTwoPair(evaluationSet)) != null) {
				score = score + (dummyField[0].getCardList().get(0).getNumber() - 1)*14;//14 bis 182
				score = score + dummyField[1].getCardList().get(0).getNumber() - 1;		//1 bis 13
				evaluationSet = evaluationSet.disMerge(dummyField[0]);					//ges 15 bis 196
				evaluationSet = evaluationSet.disMerge(dummyField[1]);
				
				bestCards = bestCards + 4;

			} else if ( bestCards < 4 && (dummySet = this.checkPair(evaluationSet)) != null) {
				score = score + dummySet.getCardList().get(0).getNumber() - 1;		// 1 bis 13
				evaluationSet = evaluationSet.disMerge(dummySet);
				
				bestCards = bestCards + 2;
				
			} else if (bestCards < 5 && (dummyCard = this.checkHighCard(evaluationSet)) != null) {
				int number = dummyCard.getNumber();
				int addScore = 0;		//Max ist 7375
				
				switch (number) {
				case 14:
					addScore = 3688;
					break;
				case 13:
					addScore = 1953;
					break;
				case 12:
					addScore = 1034;
					break;
				case 11:
					addScore = 547;
					break;
				case 10:
					addScore = 290;
					break;
				case 9:
					addScore = 153;
					break;
				case 8:
					addScore = 81;
					break;
				case 7:
					addScore = 43;
					break;
				case 6:
					addScore = 22;
					break;
				case 5:
					addScore = 12;
					break;
				case 4:
					addScore = 6;
					break;
				case 3:
					addScore = 3;
					break;
				case 2:
					addScore = 2;
					break;
				}

				score = score + ((double)addScore)/7380;		// theoretisch bis 1
				evaluationSet.removeCard(dummyCard);
				
				bestCards = bestCards + 1;
			} else return 0;	//weniger als 5 Karten in der Eingabe, führt allerdings auch in anderen Methoden schon zu Fehlern
		}

		return score; // 0 = Fehler im Evaluierungsprozess
	}
		
	//Gibt die niedrigste Karte im gegeben CardSet zurück
	public Card checkLowCard(CardSet cardSet){
		
		if(cardSet == null) return null;
		
		CardSet[] lowCards = new CardSet[13];
		for(int i = 0; i < lowCards.length; i++){
			lowCards[i] = new CardSet();
		}

		for (Card card : cardSet.getCardList()) {
			lowCards[card.getNumber() - 2].addCard(card);
		}
		
		for (int i = 0; i < lowCards.length; i++) {
			if (lowCards[i].getCardList().size() == 1) {
					return lowCards[i].getCardList().get(0);
				}
			}
		return null;
	}
	
	//Gibt die höchste Karte im gegeben CardSet zurück
	public Card checkHighCard(CardSet cardSet){
		
		if(cardSet == null) return null;
		
		CardSet[] highCards = new CardSet[13];

		for(int i = 0; i < highCards.length; i++){
			highCards[i] = new CardSet();
		}
		
		for (Card card : cardSet.getCardList()) {
			highCards[card.getNumber() - 2].addCard(card);
		}
		
		for (int i = highCards.length - 1; i >= 0; i--) {
			if (highCards[i].getCardList().size() == 1) {
					return highCards[i].getCardList().get(0);
				}
			}
		return null;
	}
	
	//Gibt das größte Paar im gegebenen CardSet zurück
	public CardSet checkPair(CardSet cardSet){
		
		if(cardSet == null) return null;
		
		CardSet[] pairs = new CardSet[13];
		for(int i = 0; i < pairs.length; i++){
			pairs[i] = new CardSet();
		}

		for (Card card : cardSet.getCardList()) {
			pairs[card.getNumber() - 2].addCard(card);
		}
		
		for (int i = pairs.length - 1; i >= 0; i--) {
			if (pairs[i].getCardList().size() == 2) {
					return pairs[i];
				}
			}
		return null;
	}
	
	//Gibt die zwei höchsten Paare im gegeben CardSet geordnet zurück, 0. Eintrag ist das größere Paar
	public CardSet[] checkTwoPair(CardSet cardSet) {
		
		if(cardSet == null) return null;
		
		CardSet[] checkPairs = new CardSet[13];
		for(int i = 0; i < checkPairs.length; i++){
			checkPairs[i] = new CardSet();
		}
		CardSet pair1 = null;
		CardSet pair2 = null;

		for (Card card : cardSet.getCardList()) {
			checkPairs[card.getNumber() - 2].addCard(card);
		}
		
		for (int i = checkPairs.length - 1; i >= 0; i--) {
			if (checkPairs[i].getCardList().size() == 2) {
					if(pair1 == null){
						pair1 = checkPairs[i];
					}
					else if(pair2 == null)
					{
						pair2 = checkPairs[i];
					}
				}
			}
		if(pair1 != null && pair2 != null)
		{
			return new CardSet[]{pair1, pair2};		//pair1 ist immer das größte
		}
		return null;
	}
	
	//Überprüft ob ein Drilling vorhanden ist und gibt den höchsten Drilling aus
	public CardSet checkDrilling(CardSet cardSet){
		
		if(cardSet == null) return null;
		
		CardSet[] drillings = new CardSet[13];
		for(int i = 0; i < drillings.length; i++){
			drillings[i] = new CardSet();
		}

		for (Card card : cardSet.getCardList()) {
			drillings[card.getNumber() - 2].addCard(card);
		}
		
		for (int i = drillings.length - 1; i >= 0; i--) {
			if (drillings[i].getCardList().size() == 3) {
					return drillings[i];
				}
			}
		return null;
	}
	
	//Gibt die höchste Straight (5 Karten) geordnet aus, mit der höchsten Karte im 0. Eintrag der CardArrayList
	public CardSet checkStraight(CardSet cardSet){
		
		if(cardSet == null) return null;
		
		CardSet[] analyse = new CardSet[13];
		for(int i = 0; i < analyse.length; i++){
			analyse[i] = new CardSet();
		}
		CardSet straight = new CardSet();
		int straightCount = 0;

		for (Card card : cardSet.getCardList()) {
			analyse[card.getNumber() - 2].addCard(card);
		}
		for(int i = analyse.length - 1; i >= 0; i--){
			if(analyse[i].getCardList().size() >= 1 && straightCount < 5)
			{
				straight.addCard(analyse[i].getCardList().get(0));
				straightCount += 1;
			}
			else if(straightCount == 5) {
				return straight;
			}
			else {
				straight = new CardSet();
				straightCount = 0;
			}
		}
		//Ass unter 2
		if(straightCount == 4 && analyse[12].getCardList().size() >= 1){
			straight.addCard(analyse[12].getCardList().get(0));
			straightCount += 1;
		}
		
		if(straightCount == 5){
			return straight;
		}
		else {
			return null;
		}
	}
	
	//Gibt den besten im Kartenset enhaltenen Flush (5 Karten) zurück
	public CardSet checkFlush(CardSet cardSet){
		
		if(cardSet == null) return null;
		
		CardSet[] cardByColors = new CardSet[4]; 		//CLUB, SPADE, HEART, DIAMOND
		for(int i = 0; i < cardByColors.length; i++){
			cardByColors[i] = new CardSet();
		}
		
		
		for (Card card : cardSet.getCardList()) {
			int color = card.getColor();

			if (cardByColors[color - 1].getCardList().size() > 5) {
				Card lowCard = this.checkLowCard(cardByColors[color - 1]);
				
					if(card.getNumber() > lowCard.getNumber()){
						cardByColors[color - 1].removeCard(lowCard);
						cardByColors[color - 1].addCard(card);
					}
				
			} 
			else {
				cardByColors[color - 1].addCard(card);
			}
		}
		for (int i = 0; i < cardByColors.length; i++) {
			if(cardByColors[i].getCardList().size() == 5){
				return cardByColors[i];
			}
		}
		return null;
	}
	
	//Gibt das größte Fullhouse zurück, 0. Eintrag ist der Drilling
	public CardSet[] checkFullHouse (CardSet cardSet){
		
		if(cardSet == null) return null;
		
		CardSet drilling = this.checkDrilling(cardSet);
		
		if(drilling != null){
			CardSet newCardSet = cardSet.disMerge(drilling);
			CardSet pair = this.checkPair(newCardSet);
			
			if(pair != null){
				return new CardSet[] {drilling, pair};		//1. Eintrag Drilling, 2. Eintrag Pair
			}
		}
		
		return null;
	}
	
	//Gibt den größten Vierling im gegebenen CardSet zurück
	public CardSet checkVierling(CardSet cardSet){
		
		if(cardSet == null) return null;
		
		CardSet[] vierlings = new CardSet[13];
		for(int i = 0; i < vierlings.length; i++){
			vierlings[i] = new CardSet();
		}

		for (Card card : cardSet.getCardList()) {
			vierlings[card.getNumber() - 2].addCard(card);
		}
		
		for (int i = vierlings.length - 1; i >= 0; i--) {
			if (vierlings[i].getCardList().size() == 4) {
					return vierlings[i];
				}
			}
		return null;
	}
	
	//Gibt einen geordneten StraightFlush mit der höchsten Karte im 0. Eintrag zurück
	public CardSet checkStraightFlush(CardSet cardSet){
		
		if(cardSet == null) return null;
		
		CardSet straightFlush = this.checkFlushAll(cardSet);
		straightFlush = this.checkStraight(straightFlush);
		
		return straightFlush;
	}
	
	//Gibt alle Karten der Flushfarbe zurück, es gibt nur maximal eine Flushfarbe
	public CardSet checkFlushAll(CardSet cardSet){

		if(cardSet == null) return null;

		CardSet[] cardByColors = new CardSet[4]; 		//CLUB, SPADE, HEART, DIAMOND
		for(int i = 0; i < cardByColors.length; i++){
			cardByColors[i] = new CardSet();
		}
		
		
		for (Card card : cardSet.getCardList()) {
			int color = card.getColor();
				cardByColors[color - 1].addCard(card);
			}

		for (int i = 0; i < cardByColors.length; i++) {
			if(cardByColors[i].getCardList().size() >= 5){

				return cardByColors[i];
			}
		}
		return null;
	}
		
}

